#pragma once

struct GLUniformBuffer;
class Variable;


struct GLShader
{
	struct Sampler
	{
		std::string		name;
		uint	location;

		std::string		textureName;
		GLTexture**		textureCtn;

		std::string		samplerName;
		// TODO SAMPLERY

		Sampler() :
			location(0),
			textureCtn(NULL)
		{}
	};

	/*--------------------------------------------------------------------------*/

	struct Data
	{
		int				location;
		std::string		name;
		std::string		type;

		Data() :
			location(-1)
		{}
	};

	/*--------------------------------------------------------------------------*/

	struct Macro
	{
		std::string		name;
		std::string		value;

		Macro()
		{}
	};

	/*--------------------------------------------------------------------------*/

	struct UBO
	{
		uint		blockIndex;
		GLUniformBuffer*	ubo;

		UBO() :
			blockIndex(0),
			ubo(NULL)
		{}
	};

	/*--------------------------------------------------------------------------*/

	GLenum					type;
	std::string				source;
	std::string				compilationLog;
	uint			handle;
	uint			references;

	/*--------------------------------------------------------------------------*/
	std::vector<UBO>		data_ubo;
	uint			num_ubo;

	std::vector<Macro>		data_macros;
	uint			num_macros;

	std::vector<Data>		data_inputs;
	uint			num_inputs;

	std::vector<Data>		data_outputs;
	uint			num_outputs;

	std::vector<Sampler>	data_samplers;
	uint			num_samplers;
	/*--------------------------------------------------------------------------*/
	std::string				name;
	bool					samplersBound;
	bool					dirty;

	GLShader(const std::string& name) :
		name(name),
		handle(0),
		type(GL_VERTEX_SHADER),
		references(0),
		samplersBound(false),
		num_ubo(0),
		num_macros(0),
		num_inputs(0),
		num_outputs(0),
		num_samplers(0),
		dirty(false)
	{}
};

struct GLProgram
{
	std::vector<GLShader*>	attachedShaders;
	uint			num_attachedShaders;

	std::string				linkLog;
	std::string				name;
	uint			handle;
	bool					linked;

	GLProgram(const std::string& name) :
		name(name),
		linked(false),
		num_attachedShaders(0)
	{}
};


class ShaderWrapper
{
protected:
	ShaderWrapper();
	~ShaderWrapper();

	static int			_shadersCount;
	static int			_programsCount;
	static GLProgram*	_boundProgram;
	static std::map<std::string, GLShader*>		_shadersMap;
	static std::map<std::string, GLProgram*>	_programsMap;

public:
	static GLProgram*	GenProgram(const std::string& name);
	static void			BindProgram(GLProgram* shader);
	static void			UnbindProgram();
	static void			DeleteProgram(GLProgram** program);

	static void			AttachShader(GLProgram* program, GLShader* shader);
	static void			DetachShader(GLProgram* program, GLShader* shader);

	static GLShader*	GenShader(const std::string& name, GLenum type, const std::string& source);
	static void			DeleteShader(GLShader** shader);

	static void			UploadSamplerData();
	static void			UploadUboData();

	static void			BindUBOToShader(GLShader* shader, GLUniformBuffer* source);
	static void			BindVariableToShader(GLShader* shader, Variable* variable);
	static void			BindShaderUnit(GLShader* shader, const std::string& file, GLenum type);
	static void			BindDefineToShader(GLShader* shader, const std::string& name, const std::string& value);
	static void			BindSamplerToShader(GLShader* shader, const std::string& name);

	static void			BindInputToShader(GLShader* shader, const std::string& type, const std::string& name, int location);
	static void			BindOutputToShader(GLShader* shader, const std::string& type, const std::string& name, int location);

	static void			Compile(GLShader* shader);
	static void			Link(GLProgram* program);

	static void			ReloadShader(GLShader* shader);

	static GLShader*	GetShader(const std::string& name);
	static GLProgram*	GetProgram(const std::string& name);

	static void			OptimizeShader(GLShader* shader);
};

